Grokking System Design Fundamentals
Rate this course
Ask Author
Contribute
Back to course home

66% completed

Vote For New Content
System Design Fundamentals
Introduction to System Design
Load Balancing
Introduction to Load Balancing
Load Balancing Algorithms
Uses of Load Balancing
Load Balancer Types
Stateless vs. Stateful Load Balancing
High Availability and Fault Tolerance
Scalability and Performance
Challenges of Load Balancers
API Gateway
Introduction to API Gateway
Usage of API gateway
Advantages and disadvantages of using API gateway
Key Characteristics of Distributed Systems
Scalability
Availability
Latency and Performance
Concurrency and Coordination
Monitoring and Observability
Resilience and Error Handling
Fault Tolerance vs. High Availability
Network Essentials
HTTP vs. HTTPS
TCP vs. UDP
HTTP: 1.0 vs. 1.1 vs 2.0 vs. 3.0
URL vs. URI vs. URN
Domain Name System (DNS)
Introduction to DNS
DNS Resolution Process
DNS Load Balancing and High Availability
Caching
Introduction to Caching
Why is Caching Important?
Types of Caching
Cache Replacement Policies
Cache Invalidation
Cache Read Strategies
Cache Coherence and Consistency Models
Caching Challenges
Cache Performance Metrics
CDN
What is CDN?
Origin Server vs. Edge Server
CDN Architecture
Push CDN vs. Pull CDN
Data Partitioning
Introduction to Data Partitioning
Partitioning Methods
Data Sharding Techniques
Benefits of Data Partitioning
Common Problems Associated with Data Partitioning
Proxies
What is a Proxy Server?
Uses of Proxies
VPN vs. Proxy Server
Redundancy and Replication
What is Redundancy?
What is Replication?
Replication Methods
Data Backup vs. Disaster Recovery
CAP & PACELC Theorems
Introduction to CAP Theorem
Components of CAP Theorem
Trade-offs in CAP Theorem
Examples of CAP Theorem in Practice
Beyond CAP Theorem
System Design Trade-offs in Interviews
Databases (SQL vs. NoSQL)
Introduction to Databases
SQL Databases
NoSQL Databases
SQL vs. NoSQL
ACID vs BASE Properties
Real-World Examples and Case Studies
SQL Normalization and Denormalization
In-Memory Database vs. On-Disk Database
Data Replication vs. Data Mirroring
Database Federation
Indexes
What are Indexes?
Types of Indexes
Bloom Filters
Introduction to Bloom Filters
Benefits & Limitations of Bloom Filters
Variants and Extensions of Bloom Filters
Applications of Bloom Filters
Difference Between Long-Polling, WebSockets, and Server-Sent Events
What is Quorum?
What is Heartbeat?
What is Checksum?
Uses of Checksum
What is Leader and Follower Pattern?
What is Security and Privacy?
What is Authentication?
What is Authorization?
Authentication vs. Authorization
OAuth vs. JWT for Authentication
What is Encryption?
What are DDoS Attacks?
Introduction to Messaging System
Introduction to Kafka
Messaging patterns
Popular Messaging Queue Systems
RabbitMQ vs. Kafka vs. ActiveMQ
Scalability and Performance
What is a Distributed File System?
Architecture of a Distributed File System
Key Components of a DFS
Batch Processing vs. Stream Processing
XML vs. JSON
Synchronous vs. Asynchronous Communication
Push vs. Pull Notification Systems
Microservices vs. Serverless Architecture
Message Queues vs. Service Bus
Stateful vs. Stateless Architecture
Event-Driven vs. Polling Architecture
Quiz
Introduction to Bloom Filters

Imagine needing to check if something is in a huge collection without storing the entire collection in memory. Bloom filters offer a clever solution to this problem. A Bloom filter is a space-efficient probabilistic data structure for set membership tests – it can quickly tell you if an item is definitely not in a set or if it is probably in the set. In other words, false negatives are impossible (it never misses an item that was added), but it can sometimes return a false positive (claim an item is in the set when it isn’t, due to its probabilistic nature). This trade-off of a tiny error rate for big gains in space and speed often pays off in real-world systems where memory is precious and an occasional false alarm is acceptable.

Bloom filters were invented by Burton Bloom in 1970, but they’re more relevant than ever in today’s data-intensive applications. They shine in scenarios where maintaining a full list or hash set of all items would be impractical. Bloom filters use a fixed-size bit array as a kind of “signature” or fingerprint of a set, and they use multiple hash functions to distribute items across that fingerprint. The result is a super-fast, memory-thrifty check for membership that doesn’t grow significantly with the number of items. Before diving into use cases, let’s build an intuition for how Bloom filters work and why they sometimes say “maybe.”

What Exactly Is a Bloom Filter?

At its core, a Bloom filter is like a magical checklist that can answer questions of the form, “Have we seen this item before?” It consists of two main components:

  • Bit Array: An array of bits (0s and 1s), initially all set to 0.
  • Hash Functions: Several independent hash functions that each take an input item and produce an index (a position in the bit array).

When you add an item to a Bloom filter, the item is run through each of the hash functions to get multiple array positions, and the bits at all those positions are set to 1. To query an item (check membership), you run it through the same hash functions to get those positions and check the bits:

  • If any one of those bits is 0, the item was definitely never added (the answer is “definitely not in the set”).
  • If all of those bits are 1, then the item is probably in the set – it might have been added, but it could also be a false positive caused by some other items.

Because of this mechanism, Bloom filters guarantee no false negatives. Any item that was added will have had its bits set, so it will always test positive. However, items that were never added might by coincidence have all their bits set by other items, leading the filter to erroneously report “probably present.” We’ll explore this “maybe” answer in a moment with an analogy.

Why use multiple hash functions? Think of each hash function as giving the item a few independent “labels” or positions in the bit array. Using several different hashes spreads the influence of each item across the array. This redundancy is what makes false negatives impossible: an item would have to lose all its labels (bits) to be missed, which can’t happen unless we explicitly remove it. On the flip side, if too many items crowd the same few positions, they can collectively cause false positives by filling in all the labels for some item that wasn’t actually added.

How Bloom Filters Work (Step by Step)

Below diagram shows an example Bloom filter for a set containing three items (P, Q, R). Each item is hashed by three hash functions (indicated by different colored arrows) to specific positions in the bit array, which are then set to 1. For instance, item “P” sets three bits (the red arrows). To query a new item, you hash it and check the corresponding bit positions; if any required bit is 0, the item is definitely not in the set, but if all are 1, the item is probably in the set. For example, the item "X" is not present as it points to a "0" bit.

Bloom Filter Using 3 Hash Functions
Bloom Filter Using 3 Hash Functions

The process of using a Bloom filter can be outlined in a few simple steps:

  1. Initialize: Start with a bit array of length N, all bits set to 0. Decide on k independent hash functions (each should output a number in the range 0 to N-1).

  2. Adding an Item: To add an element to the Bloom filter, feed it into each of the k hash functions to get k array indices. For each index produced, set the bit at that position in the array to 1. (If a bit is already 1, leave it as 1.) After adding many items, some bits in the array will remain 0, and some will be 1. Each added item typically sets a few different bits to 1.

  3. Querying an Item: To check if an element might be in the set, run it through the same k hash functions to get k positions. Then check those positions in the bit array:

    • If any one of those bits is 0, you can say with certainty the item is not in the set (because if it had been added, that bit would have been set to 1).
    • If all k bits are 1, the filter’s answer is “possibly in the set.” In this case, either the item was added (and set all those bits) or those bits happened to be set by some other combination of items. So the item may be in the set, but there’s a small chance this is a false positive.
  4. Dealing with False Positives: If the Bloom filter returns "probably in set," one usually double-checks via a slower method (e.g. doing the full lookup in a database or cache) if exact confirmation is needed. The Bloom filter’s job is mainly to screen out definitively missing items quickly, to avoid wasting time on costly lookups for items that aren’t there.

These steps make Bloom filters extremely fast and memory-efficient. Notably, the time to add or check an item is O(k) – a constant, independent of the number of elements already stored. In practice k (the number of hash functions) is fixed and usually small (like 3 to 10), so Bloom filter operations take a fixed few hash computations and array accesses. This constant-time performance holds even when the set represented is huge (millions of items), which is a big advantage over structures like lists or trees for membership testing.

What about removing items? A standard Bloom filter does not support removals. Once a bit is set to 1, clearing it could remove information about other items that hashed to that bit. There are variants like counting Bloom filters that use counters instead of single bits to allow deletions, but they use more memory and complexity. For the basic Bloom filter, you can only add items (or reset everything). This is why Bloom filters are best suited for scenarios where the set only accumulates or where you can occasionally rebuild or refresh the filter if needed.

Understanding False Positives (Why “Probably?”)

Bloom filters trade perfect accuracy for efficiency. Let’s unpack the idea of a false positive with a simple analogy:

Analogy – Fingerprints on a Shared Paper: Imagine you maintain a guest log using a single large sheet of paper. Every time a new person comes in, instead of writing their name, you ask them to press their thumb on the paper in a few different places, leaving distinct fingerprints. You have a specific set of k spots on the paper that each person must mark (determined by some rule, analogous to hash functions). Now, to check if a particular person has visited before, you have them press their thumb on those same k spots and see if all those spots on the paper are already smudged. If you find any clean spot, you know that person has never been there (definitely not in the set). But if all the spots are smudged, it suggests that perhaps their prints are already on the paper – in other words, they might have visited. However, it’s possible those smudges were left by other people’s thumbs coincidentally covering the same spots. In that case, you’d mistakenly think the new person had visited when they actually hadn’t.

This metaphor captures the essence of Bloom filter false positives. The “paper” is our bit array, and each thumbprint covers several bit positions (like multiple hash outputs). If all the required bits are marked, we get a “maybe yes” – a potential false positive if the marks were actually left by others. Crucially, you’ll never incorrectly say “no” for an item that was added – if a person did press their thumb, those spots will be marked. In Bloom filter terms, once a bit is set to 1 for an added item, that bit remains set, ensuring no future query for that item will find a 0 and falsely conclude “not present.”

Why do false positives happen? It’s because different items overlap in the bits they set. With k hash functions, each item marks k bits. As more items are added, the bit array gets more filled up with 1s. Sometimes, an unlucky combination of other items can fill all the bits that a query item would require. The Bloom filter has no way to tell that those bits were set by other items, so it can only report “probably present.” Essentially, a Bloom filter compresses information about the set – that compression is what saves space, but it also means we lose the ability to distinguish some different combinations of items (hence the confusion that causes false positives).

Controlling the false positive rate: The probability of a false positive can be tuned by adjusting the size of the bit array and the number of hash functions. A larger bit array means bits are more sparsely filled for the same number of items, reducing the chance of accidental overlaps. More hash functions mean each item covers more bits (which paradoxically can either increase or decrease false positives depending on balance – too few hashes and each item doesn’t mark enough territory, too many and you fill the array too quickly). In fact, there is an optimal k for a given array size and number of items that minimizes false positives. In practical terms, designers choose parameters so that the false positive probability is very low (say 1% or 0.1% or even less) based on what’s acceptable for the application. If an application can tolerate 1 in 1000 queries being a false alarm, the Bloom filter can be sized accordingly (with more bits per item to make that unlikely). There’s a clear trade-off: more memory = fewer false positives. Many systems let you configure this trade-off; for example, Apache Cassandra (a distributed database) allows tuning the Bloom filter’s target false-positive rate, using more RAM to get it as low as needed.

In summary, false positives are the price we pay for massive space and time savings. In exchange for never missing an actual member and using very little memory, we accept a slim chance of getting a “maybe” for something that isn’t really there. In scenarios where a false positive only causes a minor extra step (like an unnecessary database lookup), Bloom filters are a huge win.

Types of Indexes
Benefits & Limitations of Bloom Filters
Mark as Completed

On this page

What Exactly Is a Bloom Filter?

How Bloom Filters Work (Step by Step)

Understanding False Positives (Why “Probably?”)